package com.framewrok.core.poi.util.office;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.PositionInParagraph;
import org.apache.poi.xwpf.usermodel.TextSegement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFNumbering;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;

import com.framewrok.core.poi.util.office.bo.WordTableBO;

@SuppressWarnings("unchecked")
public abstract class WordGenerateUtil extends WordUtil {
	private static final String REP_KEY_STR = "${@}"; // 替换占位符
	private static final String LOOP_START_STR = "<##_contain_@>";// 循环做环开始
	private static final String LOOP_END_STR = "</##_contain_@>";// 循环做环结束
	private static final Pattern REGEX_REP = Pattern.compile(REP_KEY_STR.replace("@", ".*?").replace("{", "\\{").replace("$", "\\$"));
	private static final Pattern REGEX_LOOP_START = Pattern.compile(LOOP_START_STR.replace("@", ".*?"));
	private static final Pattern REGEX_LOOP_END = Pattern.compile(LOOP_END_STR.replace("@", ".*?"));

	public static void main(String[] args) {
		Map<String, Object> data = new HashMap<String, Object>();
		{
			data.put("title", "业务的审计");
			data.put("tagert", "中国建设银行RF省分行");
		}
		{
			List<Map<String, Object>> loop = new ArrayList<Map<String, Object>>();
			{
				Map<String, Object> obj = new HashMap<String, Object>();
				obj.put("title", "基本情况");
				loop.add(obj);

				List<Map<String, Object>> loop2 = new ArrayList<Map<String, Object>>();
				Map<String, Object> obj1 = new HashMap<String, Object>();
				obj1.put("context", "计室高测试内容说明1");
				loop2.add(obj1);

				Map<String, Object> obj3 = new HashMap<String, Object>();
				obj3.put("context", "计室高测试内容说明1");
				loop2.add(obj1);
				obj.put("loopC1", loop2);

			}
			{
				Map<String, Object> obj = new HashMap<String, Object>();
				obj.put("title", "审计报告问题");
				loop.add(obj);

				List<Map<String, Object>> loop2 = new ArrayList<Map<String, Object>>();
				Map<String, Object> obj1 = new HashMap<String, Object>();
				obj1.put("context", "计室高测试内容说明1");
				loop2.add(obj1);

				Map<String, Object> obj3 = new HashMap<String, Object>();
				obj3.put("context", "计室高测试内容说明1");
				loop2.add(obj1);
				obj.put("loopC1", loop2);
			}
			data.put("loopC", loop);
		}

		{
			WordTableBO wtbo = new WordTableBO();
			List<Map<String, String>> dataitem = new ArrayList<Map<String, String>>();
			for (int i = 0; i < 5; i++) {
				Map<String, String> map = new HashMap<String, String>();
				map.put("a", "test+a");
				map.put("b", "test+b");
				map.put("c", "test+c");
				map.put("d", "test+d");
				dataitem.add(map);
			}

			for (int i = 0; i < 5; i++) {
				List<String> head = new ArrayList<String>();
				head.add("标题一");
				head.add("标题二");
				head.add("标题三");
				head.add("标题四");
				wtbo.setHead(head);
			}
			wtbo.setData(dataitem);
			data.put("table01", wtbo);
		}

		File fFile = new File("d:/export/wordCopy.docx");
		File tFile = new File("d:/export/export_rst.docx");
		exportWord(data, fFile, tFile);
	}

	/**
	 * 根据模板生成word文件<br/>
	 */
	public static void exportWord(Map<String, Object> data, File fFile, File tFile) {
		XWPFDocument fdoc = readDoc(fFile);
		XWPFDocument tdoc = new XWPFDocument() {

			@Override
			public List<IBodyElement> getBodyElements() {
				return bodyElements;
			}

		};
		List<IBodyElement> bodysf = fdoc.getBodyElements();
		copyStyle(fdoc, tdoc);
		generator(tdoc, bodysf, data, 0, bodysf.size() - 1);
		writeDoc(tFile, tdoc);
	}

	/**
	 * 递归生成word 入口不要修改
	 */
	private static void generator(XWPFDocument tdoc, List<IBodyElement> fbodys, Map<String, Object> data, int start, int end) {
		IBodyElement fbe = null;
		String key = null;
		int counttb = 0;
		for (int i = start; i <= end; i++) {
			fbe = fbodys.get(i);
			switch (fbe.getElementType()) {
			case PARAGRAPH:
				XWPFParagraph fph = XWPFParagraph.class.cast(fbe);
				String text = fph.getParagraphText();
				text = text == null ? null : text.trim();
				List<String> loopstart = loopLabel(text, true);
				List<String> loopsend = loopLabel(text, false);
				if (loopstart.size() == 0 && loopsend.size() == 0) {// 段落内容
					copyParagraph(tdoc, fph, data);

				} else if (loopstart.size() == 1 && loopsend.size() == 0) {// 循环区域
					key = loopstart.get(0);
					if (!LOOP_START_STR.replace("@", key).equals(text)) {
						throw new RuntimeException("now not supperted for loop label[" + key + "] too many info ");
					}
					int endb = looplabelE(key, fbodys, i, end);
					if (data.get(key.trim()) == null) {
						i = endb + 1;
					} else if (data.get(key) instanceof List) {
						List<Map<String, Object>> vals = List.class.cast(data.get(key));
						for (int o = 0; o < vals.size(); o++) {
							generator(tdoc, fbodys, vals.get(o), i + 1, endb - 1);
						}
					} else {
						throw new RuntimeException("loop data not math lable[" + key + "] list value");
					}
				} else if (loopstart.size() == 0 && loopsend.size() != 0) {

				} else {
					throw new RuntimeException("loop data too many lable, values[" + text + "]");
				}
				break;
			case TABLE:
				XWPFTable ftb = XWPFTable.class.cast(fbe);
				++counttb;
				key = tableLabel(ftb);
				if (key == null || key.trim().length() == 0) {
					throw new RuntimeException("generator tale in site[" + counttb + "] no title please set");
				}
				WordTableBO ldata = WordTableBO.class.cast(data.get(key));
				copyTable(tdoc, ftb, ldata);
				break;
			default:
				throw new RuntimeException("now not suppert element type of word ");
			}
		}
	}

	/************* copy ****************/

	// word文档样式复制
	private static void copyStyle(XWPFDocument docf, XWPFDocument doct) {
		try {
			CTStyles fctstyles = docf.getStyle();
			XWPFStyles swfnew = doct.createStyles();
			swfnew.setStyles(fctstyles);
			XWPFNumbering tmpnumber = docf.getNumbering();
			if (tmpnumber == null)
				return;
			XWPFNumbering number = doct.createNumbering();
			BigInteger levl = null;
			for (int i = 0; i < 100; i++) {
				levl = new BigInteger(i + "");
				if (tmpnumber.getAbstractNum(levl) != null) {
					number.addAbstractNum(tmpnumber.getAbstractNum(levl));
					number.addNum(levl);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void copyParagraph(XWPFDocument tdoc, XWPFParagraph fph, Map<String, Object> data) {
		CTP tctp = null;
		try {
			tctp = CTP.Factory.parse(fph.getCTP().xmlText());
		} catch (XmlException e) {
			throw new RuntimeException();
		}

		XWPFParagraph tph = new XWPFParagraph(tctp, tdoc);
		removeR(tph);
		String text = fph.getParagraphText();
		text = text == null ? null : text.trim();
		List<String> replaces = replaceLabel(text);
		String val = null;
		String key = null;
		for (int j = 0; replaces != null && j < replaces.size(); j++) {
			key = replaces.get(j);
			val = toStr(data.get(key));
			if (val != null) {
				replaceTxt(key, val, tph);
			}
		}
		// System.out.println(tph.getParagraphText());
		XWPFParagraph ttph = tdoc.createParagraph();
		int post = tdoc.getPosOfParagraph(ttph);
		int tpost = tdoc.getParagraphPos(post);
		tdoc.setParagraph(tph, tpost);
		/**
		 * // paragraph properties copy.. if (fph.getCTP().isSetPPr()) { if
		 * (!tph.getCTP().isSetPPr()) { tph.getCTP().addNewPPr(); }
		 * tph.getCTP().setPPr(fph.getCTP().getPPr()); }
		 * tph.setStyle(fph.getStyle());
		 * 
		 * // 增加换页，换行等 tph.getCTP().setRArray(fph.getCTP().getRArray());
		 * tph.setFirstLineIndent(fph.getFirstLineIndent());
		 * tph.setIndentationFirstLine(fph.getIndentationFirstLine());
		 * tph.setAlignment(fph.getAlignment());
		 * tph.setVerticalAlignment(fph.getVerticalAlignment());
		 * tph.setBorderBetween(fph.getBorderBetween());
		 * tph.setBorderBottom(fph.getBorderBottom());
		 * tph.setBorderLeft(fph.getBorderLeft());
		 * tph.setBorderRight(fph.getBorderRight());
		 * tph.setBorderTop(fph.getBorderTop());
		 * tph.setPageBreak(fph.isPageBreak());
		 * 
		 * // run properties copy.. List<XWPFRun> runs = fph.getRuns(); for (int
		 * i = 0; runs != null && i < runs.size(); i++) { copy(runs.get(i),
		 * tph.createRun()); }
		 * 
		 **/

	}

	private static void removeR(XWPFParagraph tph) {
		if (tph.getCTP().isSetRsidR()) {
			tph.getCTP().unsetRsidR();
		}
		if (tph.getCTP().isSetRsidRDefault()) {
			tph.getCTP().unsetRsidRDefault();
		}
		if (tph.getCTP().isSetRsidP()) {
			tph.getCTP().unsetRsidP();
		}
	}

	private static void removeR(XWPFTableRow row) {
		List<XWPFParagraph> phs = null;
		List<XWPFRun> runs = null;
		XWPFRun trun = null;
		for (int f = 0; row != null && row.getTableCells() != null && f < row.getTableCells().size(); f++) {
			phs = row.getCell(f).getParagraphs();
			for (int i = 0; i < phs.size(); i++) {
				removeR(phs.get(i));
				runs = phs.get(i).getRuns();
				for (int j = 0; j < runs.size(); j++) {
					trun = runs.get(j);
					if (trun.getCTR().isSetRsidDel()) {
						trun.getCTR().unsetRsidDel();
					}
					if (trun.getCTR().isSetRsidRPr()) {
						trun.getCTR().unsetRsidRPr();
					}
				}
			}
		}
	}

	// COPY TABLE
	@SuppressWarnings("deprecation")
	public static void copyTable(XWPFDocument tdoc, XWPFTable ftb, WordTableBO data) {
		try {
			XWPFTable ttb = tdoc.createTable();
			XWPFTable ttb_ = new XWPFTable(CTTbl.Factory.parse(ftb.getCTTbl().xmlText()), tdoc);
			// 初始化表头操作
			{
				XWPFTableRow rowHead = ttb_.getRow(0);
				CTTblGrid grid = ttb_.getCTTbl().getTblGrid();
				CTRow ctrowHead = rowHead.getCtRow();
				if (ctrowHead.isSetRsidTr()) {
					ctrowHead.unsetRsidTr();
				}
				removeR(rowHead);
				String cellXmlHead = rowHead.getCell(0).getCTTc().xmlText();
				int cellcountHead = rowHead.getTableCells().size();
				int headSize = data.getHead().size();
				if (headSize >= cellcountHead) {
					for (int i = cellcountHead; i < headSize; i++) {
						CTTc cttcHead = CTTc.Factory.parse(cellXmlHead);
						if (rowHead.getCell(0).getParagraphs().get(0).getCTP().isSetRsidRPr()) {
							rowHead.getCell(0).getParagraphs().get(0).getCTP().unsetRsidRPr();
						}
						System.out.println(rowHead.getCell(0).getCTTc() + "\n============");
						XWPFTableCell cellHead = new XWPFTableCell(cttcHead, rowHead, ttb.getBody());
						rowHead.addNewTableCell();
						rowHead.getTableCells().set(rowHead.getTableCells().size() - 1, cellHead);
						ctrowHead.setTcArray(ctrowHead.sizeOfTcArray() - 1, cttcHead);
						grid.addNewGridCol();
						grid.setGridColArray(grid.getGridColArray().length - 1, grid.getGridColArray()[0]);
					}
				} else if (headSize < cellcountHead) {
					while (rowHead.getTableCells().size() > headSize) {
						rowHead.removeCell(headSize - 1);
						ctrowHead.removeTc(headSize - 1);
					}
				}
			}
			ttb_.getCTTbl().getTblPr().unsetTblW();
			// 设置标题
			XWPFTableRow rowHead = ttb_.getRow(0);
			for (int i = 0; i < rowHead.getTableCells().size(); i++) {
				while (rowHead.getCell(i).getParagraphs().size() > 1) {
					rowHead.getCell(i).removeParagraph(1);
				}
				XWPFParagraph p = rowHead.getCell(i).getParagraphs().get(0);
				while (p.getRuns().size() > 1) {
					p.removeRun(1);
				}
				// removeR(p);
				p.getRuns().get(0).setText("sb" + i, 0);
				// System.out.println(p.getRuns().get(0).getCTR() +
				// "\n=========");
			}

			// 初始化表数据列
			{
				XWPFTableRow rowData = ttb_.getRow(1);
				CTRow ctrowData = rowData.getCtRow();
				String cellXmlData = rowData.getCell(0).getCTTc().xmlText();
				int cellcountData = rowData.getTableCells().size();
				int dataSize = data.getHead().size();
				if (dataSize >= cellcountData) {
					for (int i = cellcountData; i < dataSize; i++) {
						{
							CTTc cttcHead = CTTc.Factory.parse(cellXmlData);
							XWPFTableCell cellHead = new XWPFTableCell(cttcHead, rowData, ttb.getBody());
							rowData.addNewTableCell();
							rowData.getTableCells().set(rowData.getTableCells().size() - 1, cellHead);
							ctrowData.setTcArray(ctrowData.sizeOfTcArray() - 1, cttcHead);
						}
					}
				} else if (dataSize < cellcountData) {
					while (rowData.getTableCells().size() > dataSize) {
						rowData.removeCell(dataSize - 1);
						ctrowData.removeTc(dataSize - 1);
					}
				}
			}

			int post = tdoc.getPosOfTable(ttb);
			int tpost = tdoc.getTablePos(post);
			tdoc.setTable(tpost, ttb_);
			tdoc.getBodyElements().set(post, ttb_);

			System.out.println(ftb.getCTTbl());
			System.out.println("===================");
			System.out.println(ttb_.getCTTbl());

		} catch (XmlException e) {
			throw new RuntimeException(e);
		}

	}

	public static void createTable(XWPFDocument doc, WordTableBO bo) {
		XWPFTable table = doc.createTable();
		List<String> head = bo.getHead();
		XWPFTableRow row = null;
		XWPFTableCell cell = null;
		Map<String, String> dataItem = null;
		XWPFParagraph ph = null;
		XWPFRun run = null;
		for (int i = 0; i < bo.getData().size() + 1; i++) {
			dataItem = i == 0 ? null : bo.getData().get(i - 1);
			row = table.getRow(i);
			row = row == null ? table.createRow() : row;
			for (int j = 0; j < head.size(); j++) {
				cell = row.getCell(j);
				cell = cell == null ? row.createCell() : cell;
				ph = cell.getParagraphs().get(0);
				if (!ph.getCTP().isSetPPr()) {
					ph.getCTP().addNewPPr();
				}
				if (!ph.getCTP().getPPr().isSetRPr()) {
					ph.getCTP().getPPr().addNewRPr();
				}
				CTJc ctjc = ph.getCTP().getPPr().addNewJc();
				ctjc.setVal(STJc.Enum.forString("center"));
				if (i == 0) {
					run = cell.getParagraphs().get(0).createRun();
					run.getCTR().addNewRPr().addNewB();
					run.setText(head.get(j));
				} else {
					run = cell.getParagraphs().get(0).createRun();
					run.setText(dataItem.get(head.get(j)), 0);
				}
				run.setFontSize(11);
				// run.setFontFamily( );
			}
		}

	}

	public static void replaceTxt(Map<String, Object> map, XWPFParagraph ph) {
		String text = ph.getParagraphText();
		if (isNotEmp(text)) {
			List<String> listrep = replaceLabel(text);
			Object obj = null;
			for (int i = 0; listrep != null && i < listrep.size(); i++) {
				obj = map == null ? null : map.get(listrep.get(i));
				replaceTxt(listrep.get(i), obj == null ? "" : obj.toString(), ph);
			}
		}
	}

	private static void replaceTxt(String key, String val, XWPFParagraph ph) {
		if (ph == null)
			return;
		if (key == null || key.trim().length() == 0)
			return;
		String text = ph.getParagraphText();
		if (text == null || text.trim().length() == 0) {
			return;
		}
		System.out.println("replace=[" + key + ":" + val + "]");
		TextSegement textSegement = null;
		XWPFRun startRun = null;
		XWPFRun endRun = null;
		String pvlval = null;
		String keyr = replaceKey(key);
		textSegement = ph.searchText(keyr, new PositionInParagraph());
		if (textSegement != null) {
			int startRunN = textSegement.getBeginRun();
			int endRunN = textSegement.getEndRun();
			if (startRunN == endRunN) {// 在一个run中可以直接替换
				startRun = ph.getRuns().get(startRunN);
				pvlval = startRun.getText(0);
				pvlval = pvlval.replace(keyr, val);
				startRun.setText(pvlval, 0);
			} else {// 不在同一个run中
				startRun = ph.getRuns().get(startRunN);
				endRun = ph.getRuns().get(endRunN);
				pvlval = firstPartOf(startRun) + val + lastPartOf(endRun);
				startRun.setText(pvlval, 0);
				// 删除跨段的文本信息
				int count = endRunN - startRunN;
				for (int i = startRunN + 1; i <= count; i++) {
					ph.removeRun(startRunN + 1);
				}
			}
		}
	}

	/******************** tools **********************/

	/**
	 * 读取word循环区域
	 * 
	 * @param text
	 * @param start
	 * @return
	 */
	private static List<String> loopLabel(String text, boolean start) {
		String[] strs = (start ? LOOP_START_STR : LOOP_END_STR).split("@");
		int len1 = strs[0].length();
		int len2 = strs[1].length();
		return matcher(text, (start ? REGEX_LOOP_START : REGEX_LOOP_END), new int[] { len1, len2 });
	}

	private static int looplabelE(String label, List<IBodyElement> bodysf, int start, int end) {
		for (int i = end; i >= start; i--) {
			String text = pText(bodysf.get(i));
			List<String> list = loopLabel(text, false);
			if (list.contains(label)) {
				return i;
			}
		}
		throw new RuntimeException("标签[" + label + "]结束位置没有找到");
	}

	/**
	 * 读取替换的标签
	 * 
	 * @param text
	 * @return
	 */
	protected static List<String> replaceLabel(String text) {
		if (text == null || text.trim().length() == 0)
			return new ArrayList<String>();
		String[] strs = REP_KEY_STR.split("@");
		int len1 = strs[0].length();
		int len2 = strs[1].length();
		return matcher(text, REGEX_REP, new int[] { len1, len2 });
	}

	/**
	 * 读取table的标签，提供取数据
	 * 
	 * @param table
	 * @return
	 */
	protected static String tableLabel(XWPFTable table) {
		CTTblPr tblPr = table.getCTTbl().getTblPr();
		String xml = tblPr.toString();
		String regx1 = "<w\\:tblCaption.*>";
		String regx2 = "\".*\"";
		Pattern p = Pattern.compile(regx1);
		Matcher m = p.matcher(xml);
		String label = null;
		String str = null;
		if (m.find()) {
			str = m.group();
			p = Pattern.compile(regx2);
			m = p.matcher(str);
			if (m.find()) {
				str = m.group();
				label = str.substring(1, str.length() - 1);
			}
		}
		return label;
	}

	/**
	 * 读取指定字符串，同是去掉前后部份，固定字符串长度
	 * 
	 * @param text
	 * @param p
	 * @param lens
	 * @return
	 */
	private static List<String> matcher(String text, Pattern p, int[] lens) {
		List<String> list = new ArrayList<String>();
		if (text == null || text.trim().length() == 0)
			return list;
		Matcher m = p.matcher(text);
		String rst = null;
		while (m.find()) {
			rst = m.group();
			rst = rst.substring(lens[0], rst.length() - lens[1]);
			list.add(rst);
		}
		return list;
	}

	private static String replaceKey(String key) {
		return REP_KEY_STR.replace("@", key);
	}

	/**
	 * 
	 * @param str
	 * @return
	 */
	protected static boolean isNotEmp(String str) {
		return str == null || str.trim().length() == 0;
	}

	private static String pText(IBodyElement i) {
		try {
			return XWPFParagraph.class.cast(i).getParagraphText();
		} catch (Exception ev) {

		}
		return null;
	}

	private static String toStr(Object obj) {
		return obj == null ? null : obj.toString();
	}

	/**
	 * 得到替换字符占位的前非替换部份
	 * 
	 * @param run
	 * @return
	 */
	private static String firstPartOf(XWPFRun run) {
		String str = run.getText(0);
		int posit = str.lastIndexOf(REP_KEY_STR.split("@")[0]);
		if (posit > -1)
			return str.substring(0, posit);
		return "";
	}

	/**
	 * 得到替换字符占位的后非替换部份
	 * 
	 * @param run
	 * @return
	 */
	private static String lastPartOf(XWPFRun run) {
		String str = run.getText(0);
		int posit = str.lastIndexOf(REP_KEY_STR.split("@")[1]);
		if (posit > -1)
			return str.substring(posit + 1);
		return "";
	}

	public static XWPFDocument readDoc(File file) {
		InputStream is = null;
		// HWPFDocument doc = new HWPFDocument(is);
		// POIXMLDocument poix = null;
		try {
			is = new FileInputStream(file);
			XWPFDocument docTmp = new XWPFDocument(is);
			return docTmp;
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (is != null)
				try {
					is.close();
				} catch (IOException e) {
				}
		}
	}

	public static void writeDoc(File file, POIXMLDocument doc) {
		FileOutputStream os = null;
		try {
			os = new FileOutputStream(file);
			doc.write(os);
			os.close();
			os = null;
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (os != null)
				try {
					os.close();
				} catch (Exception e) {

				}

		}
	}

}
